package struts.utility;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

/**
 * 扫描指定包下的所有class
 */
public class ClasspathPackageScanner  {
    

    private String basePackage;
    private ClassLoader classLoader;
    
    public ClasspathPackageScanner(String basePackage) {
        this.basePackage = basePackage;
        this.classLoader = this.getClass().getClassLoader();

    }
    
    public ClasspathPackageScanner(String basePackage, ClassLoader classLoader) {
        this.basePackage = basePackage;
        this.classLoader = classLoader;
    }
    
    /**
     * 测试
     */
    public static void main(String[] args) throws Exception {
    	ClasspathPackageScanner scan = new ClasspathPackageScanner("com.xsoft");
        List<String> nameList=scan.getFullyQualifiedClassNameList();
    }
    
    public static List<String> getClassNames(String packageName) throws Exception {
    	ClasspathPackageScanner scan = new ClasspathPackageScanner(packageName);
        List<String> nameList=scan.getFullyQualifiedClassNameList();
        return nameList;
    }

    public List<String> getFullyQualifiedClassNameList() throws IOException {
        System.out.println("开始扫描包{"+basePackage+"}下的所有类");
        return doScan(basePackage, new ArrayList<String>());
    }

    private List<String> doScan(String basePackage, List<String> nameList) throws IOException {
        String splashPath = StringUtil.dotToSplash(basePackage);

        URL url = classLoader.getResource(splashPath);
        String filePath = StringUtil.getRootPath(url);


        List<String> names = null; 
        if (isJarFile(filePath)) {
            System.out.println("{"+filePath+"} 是一个JAR包");

            names = readFromJarFile(filePath, splashPath);
        } else {
            System.out.println("{"+filePath+"} 是一个目录");
            names = readFromDirectory(filePath);
        }

        for (String name : names) {
            if (isClassFile(name)) {
                nameList.add(toFullyQualifiedName(name, basePackage));
                System.out.println("找到{"+name+"}");
            } else {
                doScan(basePackage + "." + name, nameList);
            }
        }

        return nameList;
    }

    private String toFullyQualifiedName(String shortName, String basePackage) {
        StringBuilder sb = new StringBuilder(basePackage);
        sb.append('.');
        sb.append(StringUtil.trimExtension(shortName));

        return sb.toString();
    }

    private List<String> readFromJarFile(String jarPath, String splashedPackageName) throws IOException {
        System.out.println("从JAR包中读取类: {"+jarPath+"}");

        JarInputStream jarIn = new JarInputStream(new FileInputStream(jarPath));
        JarEntry entry = jarIn.getNextJarEntry();

        List<String> nameList = new ArrayList<>();
        while (null != entry) {
            String name = entry.getName();
            if (name.startsWith(splashedPackageName) && isClassFile(name)) {
                nameList.add(name);
            }

            entry = jarIn.getNextJarEntry();
        }

        return nameList;
    }

    private List<String> readFromDirectory(String path) {
        File file = new File(path);
        String[] names = file.list();

        if (null == names) {
            return null;
        }

        return Arrays.asList(names);
    }

    private boolean isClassFile(String name) {
        return name.endsWith(".class");
    }

    private boolean isJarFile(String name) {
        return name.endsWith(".jar");
    }

}